#include "websocket_client.h"

// This message handler will be invoked once for each incoming message. It
// prints the message and then sends a copy of the message back to the server.
void on_message(WebsocketClient* c, websocketpp::connection_hdl hdl, message_ptr msg) {
	std::cout << "on_message called with hdl: " << hdl.lock().get()
		<< " and message: " << msg->get_payload()
		<< std::endl;
	websocketpp::lib::error_code ec;
	// c->send(hdl, msg->get_payload(), msg->get_opcode(), ec);
	if (ec) {
		std::cout << "Echo failed because: " << ec.message() << std::endl;
	}
	if (c->m_onread != nullptr) {
		c->m_onread(c, msg->get_payload());
	}
}


int WebsocketClient::SendMsg(const char* str, uint32_t len,
	websocketpp::frame::opcode::value opcode)
{
	if (this->m_status != WebsocketClient::CONNECTED)
		return -1;
	if (m_tls) {
		websocketpp::lib::error_code ec;
		this->m_client_tls.send(m_conn_tls, str, len, opcode, ec);
		if (ec) {
			std::cout << "Echo failed because: " << ec.message() << std::endl;
			return -1;
		}
	}
	else {
		websocketpp::lib::error_code ec;
		this->m_client.send(m_conn, str, len, opcode, ec);
		if (ec) {
			std::cout << "Echo failed because: " << ec.message() << std::endl;
			return -1;
		}
	}
	return 0;
}


void on_open(WebsocketClient* c, websocketpp::connection_hdl hdl) {
	if (c->m_tls) {
		TlsClient::connection_ptr con = c->m_client_tls.get_con_from_hdl(hdl);
		uint32_t usocket = con->get_raw_socket().native_handle();
		auto m_server = con->get_response_header("Server");
		std::cout << "open from server" << m_server << std::endl;
		c->m_status = WebsocketClient::CONNECTED;
		if (c->m_on_connected != nullptr) {
			c->m_on_connected(c);
		}
	}
	else {
		Client::connection_ptr con = c->m_client.get_con_from_hdl(hdl);
		uint32_t usocket = con->get_raw_socket().native_handle();
		auto m_server = con->get_response_header("Server");
		std::cout << "open from server" << m_server << std::endl;
		c->m_status = WebsocketClient::CONNECTED;
		if (c->m_on_connected != nullptr) {
			c->m_on_connected(c);
		}
	}
}



void on_close(WebsocketClient* c, websocketpp::connection_hdl hdl) {
	// m_status = "Open";

	// client::connection_ptr con = c->get_con_from_hdl(hdl);
	// m_server = con->get_response_header("Server");
	c->m_status = WebsocketClient::CLOSED;
	std::cout << "on_close" << std::endl;
	if (c->m_on_disconnected != nullptr) {
		c->m_on_disconnected(c, WebsocketClient::CloseReason::LOCAL_CLOSED);
	}
}

void on_fail(WebsocketClient* c, websocketpp::connection_hdl hdl) {
	std::cout << "on_fail" << std::endl;
	Client::connection_ptr con = c->m_client.get_con_from_hdl(hdl);
	auto state = con->get_state();
	if (state == websocketpp::session::state::closed)
		std::cout << state << " on_fail " << std::endl;
	c->m_status = WebsocketClient::FAIL;
	if (c->m_on_disconnected != nullptr) {
		c->m_on_disconnected(c, WebsocketClient::CloseReason::PEER_CLOSED);
	}
}

WebsocketClient::~WebsocketClient() {

	this->m_status = WebsocketClient::STOP;
	m_client.stop();
	m_thread->join();
}

WebsocketClient::WebsocketClient(std::string url, bool tls)
{
	m_tls = tls;
	m_auto_reconn = false;
	m_url = url;
	this->m_status = WebsocketClient::CONNECTING;

	if (m_tls) {
		// Set logging to be pretty verbose (everything except message payloads)
		m_client_tls.set_access_channels(websocketpp::log::alevel::all);
		m_client_tls.clear_access_channels(websocketpp::log::alevel::frame_payload);
		m_client_tls.set_tls_init_handler([this](websocketpp::connection_hdl) {
			return websocketpp::lib::make_shared<asio::ssl::context>(asio::ssl::context::tlsv1);
			});
		// Initialize ASIO
		m_client_tls.init_asio();
		m_thread = new std::thread([this]()
			{
				while (this->m_status != STOP)
				{
					m_client_tls.set_message_handler(bind(&on_message, this, ::_1, ::_2));
					websocketpp::lib::error_code ec;
					std::cout << "1" << std::endl;
					m_conn_tls = m_client_tls.get_connection(this->m_url, ec);
					m_conn_tls->set_open_handler(websocketpp::lib::bind(
						&on_open,
						this,
						websocketpp::lib::placeholders::_1
					));
					m_conn_tls->set_fail_handler(websocketpp::lib::bind(
						&on_fail,
						this,
						websocketpp::lib::placeholders::_1
					));
					m_conn_tls->set_close_handler(websocketpp::lib::bind(
						&on_close,
						this,
						websocketpp::lib::placeholders::_1
					));
					if (ec) {
						std::cout << "could not create connection because: " << ec.message() << std::endl;
						this->m_status = Status::FAIL;
					}
					std::cout << "2" << std::endl;

					// Note that connect here only requests a connection. No network messages are
					// exchanged until the event loop starts running in the next line.
					TlsClient::connection_ptr ptr = m_client_tls.connect(m_conn_tls);
					if (ptr->get_state() != websocketpp::session::state::open)
						std::cout << ptr->get_state() << " websocketpp::session::state " << std::endl;
					// Start the ASIO io_service run loop
					// this will cause a single connection to be made to the server. c.run()
					// will exit when this connection is closed.
					std::cout << "3 " << ptr << " " << m_conn_tls << std::endl;
					this->m_status = WebsocketClient::CONNECTING;
					while ((this->m_status != WebsocketClient::FAIL) &&
						(this->m_status != WebsocketClient::CLOSED)
						&& (this->m_status != WebsocketClient::STOP)
						) {
						try {
							// while(this->m_status == WebsocketClient::CONNECTED){
							int count_of_handler = this->m_client_tls.run();
							// std::cout<<"count_of_handler:  " << count_of_handler<<std::endl;
						// }
						// run应该只执行一次就会退出
						}
						catch (std::exception e) {
							std::cout << "run exception" << e.what();
						}
					}
					Sleep(1000);
				}
				std::cout << "close";
				this->m_on_disconnected(this, WebsocketClient::CloseReason::LOCAL_CLOSED);
			});
	}
	else {
		// Set logging to be pretty verbose (everything except message payloads)
		m_client.set_access_channels(websocketpp::log::alevel::all);
		m_client.clear_access_channels(websocketpp::log::alevel::frame_payload);

		// Initialize ASIO
		this->m_client.init_asio();
		m_thread = new std::thread([this]()
			{
				while (this->m_status != STOP)
				{
					this->m_client.set_message_handler(bind(&on_message, this, ::_1, ::_2));
					websocketpp::lib::error_code ec;
					std::cout << "1" << std::endl;
					m_conn = m_client.get_connection(this->m_url, ec);
					if (m_conn != nullptr) {
						m_conn->set_open_handler(websocketpp::lib::bind(
							&on_open,
							this,
							websocketpp::lib::placeholders::_1
						));
						m_conn->set_fail_handler(websocketpp::lib::bind(
							&on_fail,
							this,
							websocketpp::lib::placeholders::_1
						));
						m_conn->set_close_handler(websocketpp::lib::bind(
							&on_close,
							this,
							websocketpp::lib::placeholders::_1
						));
					}
					if (ec) {
						std::cout << "could not create connection because: " << ec.message() << std::endl;
						this->m_status = Status::FAIL;
						if (m_on_disconnected)
							m_on_disconnected(this, WebsocketClient::CloseReason::LOCAL_CLOSED);
						break;
					}
					std::cout << "2" << std::endl;

					// Note that connect here only requests a connection. No network messages are
					// exchanged until the event loop starts running in the next line.
					Client::connection_ptr ptr = this->m_client.connect(m_conn);
					if (ptr->get_state() != websocketpp::session::state::open)
						std::cout << ptr->get_state() << " websocketpp::session::state " << std::endl;
					// Start the ASIO io_service run loop
					// this will cause a single connection to be made to the server. c.run()
					// will exit when this connection is closed.
					std::cout << "3 " << ptr << " " << m_conn_tls << std::endl;
					this->m_status = WebsocketClient::CONNECTING;
					while ((this->m_status != WebsocketClient::FAIL) &&
						(this->m_status != WebsocketClient::CLOSED)
						&& (this->m_status != WebsocketClient::STOP)
						) {
						try {
							// while(this->m_status == WebsocketClient::CONNECTED){
							int count_of_handler = this->m_client.run();
							// std::cout<<"count_of_handler:  " << count_of_handler<<std::endl;
						// }
						// run应该只执行一次就会退出
						}
						catch (std::exception e) {
							std::cout << "run exception" << e.what();
						}
					}
					Sleep(1000);
				}
				std::cout << "close";
				if(m_on_disconnected)
					m_on_disconnected(this, WebsocketClient::CloseReason::LOCAL_CLOSED);
			});
	}

}

int WebsocketClient::SetOnConnectedHandler(OnConnectedHandler on_connected) {
	this->m_on_connected = on_connected;
	return 0;
}

int WebsocketClient::SetOnDisConnectedHandler(OnDisConnectedHandler on_disconnected) {
	this->m_on_disconnected = on_disconnected;
	return 0;
}

int WebsocketClient::SetOnReadHandler(OnReadHandler onread) {
	this->m_onread = onread;
	return 0;
}
